package com.schoolegard.services.impl;

import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

import com.schoolegard.services.AlarmHandleService;
import com.schoolegard.services.AlarmLogService;
import com.schoolegard.services.MailService;
import com.schoolegard.services.RoleService;
import com.schoolgard.common.MailInfo;
import com.schoolgard.dao.AlarmLogDao;
import com.schoolgard.dao.DeviceDao;
import com.schoolgard.dao.HdStateDao;
import com.schoolgard.dao.LocationDao;
import com.schoolgard.dao.RoleToUsersDao;
import com.schoolgard.entitybean.Alarm4MapBean;
import com.schoolgard.entitybean.AlarmHandle;
import com.schoolgard.entitybean.AlarmLog;
import com.schoolgard.entitybean.Device;
import com.schoolgard.entitybean.GridDataModel;
import com.schoolgard.entitybean.HdState;
import com.schoolgard.entitybean.Location;
import com.schoolgard.entitybean.Role;
import com.schoolgard.entitybean.RoleOfUsers;
import com.schoolgard.entitybean.Terminal;
import com.schoolgard.entitybean.Users;
import com.supersystem.utils.DateUtil;

public class AlarmLogServiceImpl implements AlarmLogService {

	private AlarmLogDao alarmLogDao;
	
	private LocationDao locationDao;

	private MailService mailService;

	private AlarmHandleService alarmHandleService;

	private RoleService roleService;
	
	private RoleToUsersDao roleToUsersDao;
	
	private DeviceDao deviceDao;
	
	private HdStateDao hdStateDao;

	public GridDataModel<AlarmLog> find4Page(AlarmLog alarmLog,Users user,int first, int max) {
		DetachedCriteria criteria = DetachedCriteria.forClass(AlarmLog.class);
		criteria.createAlias("device", "device");
		criteria.createAlias("alarmClass", "alarmClass");
		criteria.createAlias("device.organization", "organization");
		try{
		if(alarmLog != null){
			if(StringUtils.isNotEmpty(alarmLog.getStatus())){
				criteria.add(Restrictions.eq("status", alarmLog.getStatus()));
			}
			if(alarmLog.getDatetime() != null) {
				criteria.add(Restrictions.ge("datetime", alarmLog.getDatetime()));
			}
			if(alarmLog.getConfirmDatetime() != null) {
				criteria.add(Restrictions.lt("datetime", DateUtils.addDays(alarmLog.getConfirmDatetime(), 1)));
			}
			if(alarmLog.getDevice() != null && StringUtils.isNotEmpty(alarmLog.getDevice().getGuid())) {
				criteria.add(Restrictions.eq("device.guid", alarmLog.getDevice().getGuid()));
			}
			if(alarmLog.getAlarmClass() != null) {
				if(StringUtils.isNotEmpty(alarmLog.getAlarmClass().getLevel())) {
					criteria.add(Restrictions.eq("alarmClass.level", alarmLog.getAlarmClass().getLevel()));
				}
				if(alarmLog.getAlarmClass().getType() != null) {
					criteria.add(Restrictions.eq("alarmClass.type", alarmLog.getAlarmClass().getType()));
				}
			}
			getIds(alarmLog.getUserName(),criteria,user);
		}else{
			if(!isAdmin(user)) {
				criteria.add(Restrictions.in("device.organization.id", getOrganizations(user.getOrganization().getId())));
			}
		}
		criteria.addOrder(Order.desc("id"));
		List<AlarmLog> logs = alarmLogDao.findByCriteria(criteria, first, max);
		int total =alarmLogDao.getRowCount(criteria);
		GridDataModel<AlarmLog> page = new GridDataModel<AlarmLog>();
		page.setPage(first / max + 1);
		page.setRows(logs);
		page.setTotal(total);
		return page;
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
	}

	private boolean isAdmin(Users user){
		if(user == null ) {
			return false;
		}
		String roles = "";
		List<RoleOfUsers> roleOfUsers = new ArrayList<RoleOfUsers>(user.getRoleOfUserses());
		for(RoleOfUsers ru : roleOfUsers) {
			if(roles.length() > 0){
				roles = roles + "," + ru.getRole();
			} else {
				roles = ru.getRole();
			}
		}
		String [] roleArray = StringUtils.split(roles, ",");
		Role admin = roleService.findByName("管理员");
		if(admin == null) {
			return false;
		}
		for(String id : roleArray) {
			if(id.equals(admin.getId().toString())) {
				return true;
			}
		}
		return false;
	}
	
	private void getIds(String id,DetachedCriteria criteria,Users user){
		if(StringUtils.isEmpty(id) || id.indexOf("_") < 0) {
			if(!isAdmin(user)) {
				criteria.add(Restrictions.in("device.organization.id", getOrganizations(user.getOrganization().getId())));
			}
			return;
		}
		String queryId = id.substring(id.indexOf("_")+1);
		String type = id.substring(0, 2);
		if("re".equals(type)){
			criteria.add(Restrictions.in("organization.regions.id",getRegions(Long.valueOf(queryId))));
		}else if ("or".equals(type)) {
			criteria.add(Restrictions.in("device.organization.id",getOrganizations(Long.valueOf(queryId))));
		}else if ("de".equals(type)){
			criteria.add(Restrictions.eq("device.guid", queryId));
		}else if (user != null && user.getOrganization() != null) {
			criteria.add(Restrictions.in("device.organization.id", getOrganizations(user.getOrganization().getId())));
		}
	}
	
	private List<Long> getRegions(Long id){
		try{
			String sql = "select id  FROM regions START WITH parent_ID = "+id+" CONNECT BY PRIOR ID = parent_ID";
			List<BigDecimal> list = (ArrayList<BigDecimal>)alarmLogDao.getResultBySql(sql);
			List<Long> ids = new ArrayList<Long>();
			if(list != null && list.size() > 0) {
				for(int i = 0;i < list.size();i++) {
					ids.add(list.get(i).longValue());
				}
			}
			ids.add(id);
			return ids;
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
	}
	
	private List<Long> getOrganizations(Long id){
		try{
			String sql = "SELECT id FROM organization START WITH parent_ID = "+id+" CONNECT BY PRIOR ID = parent_ID";
			List<BigDecimal> list = (ArrayList<BigDecimal>)alarmLogDao.getResultBySql(sql);
			List<Long> ids = new ArrayList<Long>();
			if(list != null && list.size() > 0) {
				for(int i = 0;i < list.size();i++) {
					ids.add(list.get(i).longValue());
				}
			}
			ids.add(id);
			return ids;
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
	}
	
	public void setAlarmLogDao(AlarmLogDao alarmLogDao) {
		this.alarmLogDao = alarmLogDao;
	}

	public void update(AlarmLog alarmLog) {
		this.alarmLogDao.modify(alarmLog);
	}

	public boolean deal(Long id ,Users user) {
		AlarmLog alarmLog = findById(id);
		if (alarmLog == null) {
			return false;
		}
		if(sendMail(alarmLog)) {
			alarmLog.setStatus("3");
			alarmLog.setUserId(user.getId());
			alarmLog.setUserName(user.getName());
			alarmLog.setConfirmDatetime(new Date());
			this.update(alarmLog);
			return true;
		}
		return false;
	}

	private boolean sendMail(AlarmLog alarmLog) {
		try{
			AlarmHandle alarmHandle = alarmHandleService.findByClassId(alarmLog.getAlarmClass().getId());
			String[] acceptors = StringUtils.split(alarmHandle.getAcceptor(), ",");
			List<Role> roles = roleService.findByIds(acceptors);
			if (roles == null || roles.size() == 0) {
				return false;
			}
			List<RoleOfUsers> rus = roleToUsersDao.findAll();
			MailInfo mailInfo = new MailInfo();
			for (Role role : roles) {
				List<Users> users = findUsersByRole(role,rus);
				if(users.size() == 0) {
					return false;
				}
				for (Users user : users) {
					mailInfo.addAddress(user.getMail());
				}
			}
			mailInfo.setSubject("告警派修通知");
			StringWriter sw = new StringWriter();
			String content = sw.append("设备").append(alarmLog.getDevice().getGuid())
					.append("发告警，告警内容：").append(alarmLog.getContent())
					.append(",请及时处理。").toString();
			mailInfo.setContent(content);
			return mailService.send(mailInfo);
		}catch(Exception e){
			e.printStackTrace();
			return false;
		}
	}
	
	private List<Users> findUsersByRole(Role role,List<RoleOfUsers> rus) {
		List<Users> users = new ArrayList<Users>();
		for(RoleOfUsers ru : rus){
			String roleIds = ru.getRole();
			if(StringUtils.isEmpty(roleIds)){
				continue;
			}
			String[] roles = StringUtils.split(roleIds, ",");
			for(String roleId : roles) {
				if(roleId.equals(role.getId().toString())) {
					users.add(ru.getUsers());
					continue;
				}
			}
		}
		return users;
	}

	public AlarmLog findById(Long id) {
		return this.alarmLogDao.findById(id);
	}
	
	@Override
	public List<Alarm4MapBean> findAlarm4Map() {
		//根据设备显示提示信息，有警告就显示红色，离线显示灰色，正常显示蓝色
		//设备和终端都有报警，两者没有关系
		//根据两个条件决定状态，首先是是否报警，如果报警就默认在线，不管是否在线统一显示报警，如果不报警且不在线则显示离线
		//如果不报警在线，则显示正常
		List<Alarm4MapBean> ls = findDevicealarm();
		return ls;
	}
	
	private List<Alarm4MapBean> findDevicealarm()
	{
		List<Alarm4MapBean> retLs = new ArrayList<Alarm4MapBean>();
		List<Device> devicels = deviceDao.findAll();   
		Alarm4MapBean a4b = null;
		for (Device device : devicels) {
			a4b = new Alarm4MapBean();
			Location loc = findLocation(device.getGuid());
			if(loc != null){ //只有有坐标的才显示在地图上
				AlarmLog alarmLog = findBySourceId(device.getGuid());
				HdState state = hdStateDao.findById(device.getGuid());
				if(alarmLog != null){   //如果有报警就加入报警信息，没有报警就为null，前台根据这几个字段判断是否是报警显示状态
					a4b.setAlarmContent(alarmLog.getContent());
					//a4b.setAlarmState(alarmLog.getAlarmClass().getName()+":"+alarmLog.getAlarmClass().getFormat()+" 等级："+alarmLog.getAlarmClass().getLevel());
					a4b.setDateTime(DateUtil.format(alarmLog.getDatetime()));
					a4b.setState("1");
					if(state == null){  //找不到或者不在线都认为不在线
						a4b.setState("0"); 
					}else if(!state.getOnlineStatus()){
						a4b.setState("0");
					}
				}else{
					if(state == null){  //找不到或者不在线都认为不在线
						a4b.setState("0"); 
					}else if(state.getOnlineStatus()){
						a4b.setState("2");
					}else{
						a4b.setState("0");
					}
				}
					a4b.setType("0");
					a4b.setxAxis(loc.getXAxis());
					a4b.setyAxis(loc.getYAxis());
					a4b.setObjectName(device.getName());
					a4b.setOrganization(device.getOrganization().getName());
					retLs.add(a4b);
					
					//------------------ 终端告警------------------------- //TODO 目前alarmlog表只和device关联，所以不能查询终端的alarmlog信息
					/*Set<Terminal> terls = device.getTerminals(); //获取设备
					if(terls != null && !terls.isEmpty()){
						retLs.addAll(findTerminalAlarm(terls , loc));
					}*/
			}
		}
		return retLs;
	}
	
	private List<Alarm4MapBean> findTerminalAlarm(Set<Terminal> ls , Location loc)
	{
		List<Alarm4MapBean> retLs = new ArrayList<Alarm4MapBean>();
		Iterator<Terminal> teritem = ls.iterator();
		while(teritem.hasNext()){ //终端的报警显示
			Terminal te = teritem.next();
			AlarmLog alarmLog1 = findBySourceId(te.getGuid());
			Alarm4MapBean a4b1 = new Alarm4MapBean();
			if(alarmLog1 != null){
				a4b1.setAlarmContent(alarmLog1.getContent());
				a4b1.setDateTime(DateUtil.format(alarmLog1.getDatetime()));
				a4b1.setAlarmState(alarmLog1.getAlarmClass().getName()+":"+alarmLog1.getAlarmClass().getFormat()+" 等级："+alarmLog1.getAlarmClass().getLevel());
				a4b1.setState("1");
			}else{
				HdState state = hdStateDao.findById(te.getGuid());
				if(state == null || state.getOnlineStatus()){  //找不到或者不在线都认为不在线
					a4b1.setState("0"); 
				}else{
					a4b1.setState("2");
				}
			}
			a4b1.setType("1");
			a4b1.setxAxis(loc.getXAxis());
			a4b1.setyAxis(loc.getYAxis());
			retLs.add(a4b1);
		}
		return retLs;
	}
	
	
	
	private Location findLocation(String refId)
	{
	     List<Location> locas = locationDao.findBySql("select * from LOCATION where REF_ID ='"+refId+"' and rownum = 1");
	     if(locas != null && locas.size() > 0){
	    	 return locas.get(0);
	     }else{
	    	 return null;
	     }
	}
	
	private AlarmLog findBySourceId(String guid)
	{
		List<AlarmLog> ls = alarmLogDao.findBySql("select * from  alarm_log where source_guid = '"+guid+"' and status='1' and rownum = 1 order by id desc"); //为了节约时间，取一条
		if(ls != null && ls.size() > 0){
			return ls.get(0);
		}else{
			return null;
		}
	}

	public void setMailService(MailService mailService) {
		this.mailService = mailService;
	}
	public void setAlarmHandleService(AlarmHandleService alarmHandleService) {
		this.alarmHandleService = alarmHandleService;
	}
	public void setRoleService(RoleService roleService) {
		this.roleService = roleService;
	}
	public void setLocationDao(LocationDao locationDao) {
		this.locationDao = locationDao;
	}
	public int findCount() {
		return this.alarmLogDao.getTotalCount();
	}
	public void setRoleToUsersDao(RoleToUsersDao roleToUsersDao) {
		this.roleToUsersDao = roleToUsersDao;
	}
	public void setDeviceDao(DeviceDao deviceDao) {
		this.deviceDao = deviceDao;
	}
	public void setHdStateDao(HdStateDao hdStateDao) {
		this.hdStateDao = hdStateDao;
	}

	public List<Integer> countError(AlarmLog alarmLog,Users user) {
		DetachedCriteria criteria = DetachedCriteria.forClass(AlarmLog.class);
		criteria.createAlias("device", "device");
		criteria.createAlias("alarmClass", "alarmClass");
		criteria.createAlias("device.organization", "organization");
		criteria.setProjection(Projections.projectionList()
				.add(Projections.count("alarmClass.level"))
				.add(Projections.groupProperty("alarmClass.level")));
		if(alarmLog != null){
			if(StringUtils.isNotEmpty(alarmLog.getStatus())){
				criteria.add(Restrictions.eq("status", alarmLog.getStatus()));
			}
			if(alarmLog.getDatetime() != null) {
				criteria.add(Restrictions.ge("datetime", alarmLog.getDatetime()));
			}
			if(alarmLog.getConfirmDatetime() != null) {
				criteria.add(Restrictions.lt("datetime", DateUtils.addDays(alarmLog.getConfirmDatetime(), 1)));
			}
			if(alarmLog.getDevice() != null && StringUtils.isNotEmpty(alarmLog.getDevice().getGuid())) {
				criteria.add(Restrictions.eq("device.guid", alarmLog.getDevice().getGuid()));
			}
			getIds(alarmLog.getUserName(),criteria,user);
		}else{
			criteria.add(Restrictions.in("device.organization.id", getOrganizations(user.getOrganization().getId())));
		}
		criteria.addOrder(Order.asc("alarmClass.level"));
		List<Object []> list = (List<Object []>)alarmLogDao.findByCriteriaForAnyTypeObject(criteria, 0, Integer.MAX_VALUE);
		List<Integer> result = new ArrayList<Integer>();
		if(list != null) {
			for(int k = 0; k<4;k++){
				boolean flag = false;
				for(Object[] integer : list) {
					if(Integer.valueOf(integer[1].toString()) == k+1){
						result.add((Integer)integer[0]);
						flag = true;
						break;
					}
				}
				if(!flag) {
					result.add(0);
				}
			}
		}
		return result;
	}

	public HdState findByGuid(String guid) {
		return hdStateDao.findById(guid);
	}

}
